老韩wiki.swoole.com以及一些社区中一直说swoole既可以同步又可以异步,我找一些原话,你们感受一下:

继续引用凑行数:

最后的引用:

个人认为最后这段引用是非常具备价值的,仔细品读或许能够从中得到一些感悟。我在前面曾经写过一篇,实际上你可以这么理解,就是master进程可以hold住上万个TCP连接是没有任何问题的,因为master进程内部异步非阻塞的,但是仅仅hold住上万个TCP连接本身是没有任何意义的,因为有数据传输的TCP连接才是有意义的。一旦有数据传输就意味着有业务逻辑产生了,那么master进程并不负责具体业务逻辑代码了,处理这个业务逻辑的活儿交给worker进程来干,然后干完后再由master进程返回给客户端。

同步阻塞模式下,如果说worker进程1秒钟完成1个客户端的业务逻辑,尽管master进程同时hold住了1W个TCP连接,但是1个worker进程只能服务于1个客户端,1W个客户端全部处理完毕,需要1W秒钟。所以,同步阻塞模式下,如果你想干活猛,就只能增加worker进程的数量,比如1000个甚至2000个。当然了,看到这里有为青年就会提出问题了,这样一味地增加进程数量岂不是意味着进程再多的话进程间切换都是极为耗费CPU的?是的,所以很简单,横向扩展加机器就是了16. swoole的协程是个什么鬼 - 图116. swoole的协程是个什么鬼 - 图2… …或者,选择异步。

  1. <?php
  2. // 你要创建异步的MySQL客户端,而不是普普通通的pdo mysqli
  3. $async_mysql = new async_mysql();
  4. echo '连接成功'.PHP_EOL;
  5. // 插入评论
  6. $sql = "insert into pinglun() values()";
  7. // 如果插入成功
  8. if( true == $result ){
  9. // 获取5条最新评论
  10. $sql = "select * from pinglun limit 5";
  11. $async_mysql->query( $sql, function( $async_mysql, $result ){
  12. // 获取成功后拿数据
  13. if( true == $result ){
  14. print_r( $result->fetchAll() );
  15. }
  16. } );
  17. }
  18. // 如果插入失败
  19. else {
  20. echo "插入数据失败".PHP_EOL;
  21. }
  22. });
  23. } );

这种代码里,将不可避免地产生大量的类似于on这种回调,如果再有一些条件依赖话,可能不得不层层回调。比如插入最新评论需要依赖connect,只有connect成功了才能执行插入操作,然后是查询最新5条评论功能依赖插入操作,只有插入操作成功才能继续查询5条最新评论。最重要的是,需要IO操作的这些函数等等都必须得是异步的才行,传统的pdo、mysqli是统统不可以用的。因为只要有一处是同步阻塞了,整个worker进程中的业务逻辑代码就算是彻底完蛋沦为同步阻塞了。所以说,如果你要在这种代码里用sleep( 100 ),你会死得惨烈。

“没有这金刚钻,别拦这瓷器活”…

如果说我们用传统的同步阻塞代码的话,伪代码大概如下你们感受一下:

爱不爱?喜不喜欢?高不高兴?而且我还能任意写sleep… …

当了这么多年的同步阻塞fpm(同步阻塞apache)的CURDer你跟我说你天生就爱异步?你猜我信么?

16. swoole的协程是个什么鬼 - 图3

但是,异步带来的QPS上的提升实在是太明显了(注意,异步并不能提高性能,只是能提高QPS。性能就在那里躺着呢,该是多少就是多少,只不过异步可以更好的挖掘和压榨,并不能提高TA),但异步的代码实在是难写,辣么,有没有一种既可以用同步阻塞这种风格写的背后又是异步方式的方法呢?废话,当然有,不然我要这文章有何用?这种东西就是协程!

其实,有为青年在研究Golang的时候早就已经开眼见世界了,那是身经百战见的多了,但是像我这样的蠢货萌新自然是不知道的。一些人用php的yield来实现协程,不过,我认为swoole的协程要比这个yield好很多。简单说起来,协程这个东西就是用户态的异步IO,也就说不需要操作系统参与的,这点儿上和真正的异步IO的概念是不一样的。因为严格扣定义的话,异步IO是操作系统内核实现并参与的,现在协程并不需要系统参与,仅仅用户层就可以解决这些问题。

废话不多说,还是通过代码来感受一下,这坨代码大概意思就是开了一个http服务器,开了一个worker进程,worker进程中业务逻辑代码就是往数据库里添加一条记录,你们感受一下:

首先,注释掉同步阻塞传统代码,使用协程的写法;其次,注释掉协程写法,开启同步阻塞写法。然后分别使用ab进行简单测试


这里是协程的测试结果:

这里是传统同步阻塞的测试结果:

测试结果我们就不分析了,你们应该能看懂。这中间巨大的QPS差距你们应该能感受到了。话说回来,由于我们知道想提高同步阻塞代码的QPS最有效的办法就是增加进程数量,因此我们将woker进程数量调整为8,再测试一把:

16. swoole的协程是个什么鬼 - 图4

继续调整为16:

继续调整为32(接近协程的成绩,但依然差了1000QPS):

16. swoole的协程是个什么鬼 - 图5

继续调整为64(终于超过单进程协程1600QPS了):

最终结果就是,我们用同步阻塞的模型开启了64个进程大概可以超越开启1个进程的协程方式将近1600QPS。

最后,部分有为青年可能想要了解swoole协程原理,我自己因为水准问题(其实我不懂)就不发表自己的看法了,直接盗链官网资料了: